{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "djUvWu41mtXa"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "su2RaORHpReL"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NztQK2uFpXT-"
      },
      "source": [
        "# TensorBoard のスカラー: Keras でトレーニングメトリックをログする\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/tensorboard/scalars_and_keras\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/tensorboard/scalars_and_keras.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tensorboard/scalars_and_keras.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eDXRFe_qp5C3"
      },
      "source": [
        "## 概要\n",
        "\n",
        "機械学習には、損失などの主要なメトリック、そしてトレーニングが進むにつれそれらがどのように変化するかを理解することが必ず伴います。こういったメトリックは、[過適合](https://en.wikipedia.org/wiki/Overfitting)が発生していないか、不要に長くトレーニングしていないかなどを理解する上で役立ちます。そのため、モデルのデバッグや改善を行いやすくするために、トレーニングの実行から得られるメトリックを比較すると良いでしょう。\n",
        "\n",
        "TensorBoard の **Scalars ダッシュボード**では、単純な API を使用して簡単にメトリックを視覚化することができます。このチュートリアルでは、非常に基本的な例を使って、Keras モデルを開発する際に TensorBoard でこれらの API を使用する方法を説明します。Keras TensorBoard コールバックと TensorFlow Summary API を使用してデフォルトとカスタムのスカラーを視覚化する方法を学習します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dG-nnZK9qW9z"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "3U5gdCw_nSG3"
      },
      "outputs": [],
      "source": [
        "# Load the TensorBoard notebook extension.\n",
        "%load_ext tensorboard"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "1qIKtOBrqc9Y"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TensorFlow version:  2.2\n"
          ]
        }
      ],
      "source": [
        "from datetime import datetime\n",
        "from packaging import version\n",
        "\n",
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "print(\"TensorFlow version: \", tf.__version__)\n",
        "assert version.parse(tf.__version__).release[0] >= 2, \\\n",
        "    \"This notebook requires TensorFlow 2.0 or above.\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6YDAoNCN3ZNS"
      },
      "source": [
        "## 簡単な回帰用データをセットアップする\n",
        "\n",
        "これから [Keras](https://www.tensorflow.org/guide/keras) を使用して回帰を計算し、データセットペアにおける最適な適合線を見つけたいと思います。（この種の問題でニューラルネットワークと勾配降下を使用するのは[やり過ぎ](https://stats.stackexchange.com/questions/160179/do-we-need-gradient-descent-to-find-the-coefficients-of-a-linear-regression-mode)ではありますが、例を理解しやすくなります。）\n",
        "\n",
        "TensorBoard を使用して、エポック間でトレーニングとテストの**損失**がどのように変化するのかを観測します。時間の経過とともにトレーニングとテストの損失が下降して安定化する様子を確認してください。\n",
        "\n",
        "まず、おおまかに線 *y = 0.5x + 2* に沿って 1000 個のデータポイントを生成し、それらをトレーニングセットとテストセットに分割します。ここでは、ニューラルネットワークがこの関係を学習することを目標とします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j-ryO6OxnQH_"
      },
      "outputs": [],
      "source": [
        "data_size = 1000\n",
        "# 80% of the data is for training.\n",
        "train_pct = 0.8\n",
        "\n",
        "train_size = int(data_size * train_pct)\n",
        "\n",
        "# Create some input data between -1 and 1 and randomize it.\n",
        "x = np.linspace(-1, 1, data_size)\n",
        "np.random.shuffle(x)\n",
        "\n",
        "# Generate the output data.\n",
        "# y = 0.5x + 2 + noise\n",
        "y = 0.5 * x + 2 + np.random.normal(0, 0.05, (data_size, ))\n",
        "\n",
        "# Split into test and train pairs.\n",
        "x_train, y_train = x[:train_size], y[:train_size]\n",
        "x_test, y_test = x[train_size:], y[train_size:]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Je59_8Ts3rq0"
      },
      "source": [
        "## モデルをトレーニングして損失をログする\n",
        "\n",
        "モデルの定義、トレーニング、および評価の準備が整いました。\n",
        "\n",
        "トレーニングしながら*損失*スカラーをログするには、次のように行います。\n",
        "\n",
        "1. Keras [TensorBoard コールバック](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/TensorBoard)を作成します。\n",
        "2. ログディレクトリを指定します。\n",
        "3. TensorBoard コールバックを Keras の [Model.fit()](https://www.tensorflow.org/api_docs/python/tf/keras/models/Model#fit) に渡します。\n",
        "\n",
        "TensorBoard は、ログディレクトリ階層からログデータを読み取ります。このノートブックでは、ルートログディレクトリは `logs/scalars` で、その後にタイムスタンプ付きのサブディレクトリが続きます。タイムスタンプがサブディレクトリに付加されることで、TensorBoard を使用してモデルでイテレーションを行う際に、トレーニングセットを簡単に識別して選択することができます。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "id": "VmEQwCon3i7m"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Training ... With default parameters, this takes less than 10 seconds.\n",
            "Average test loss:  0.05271831926424056\n"
          ]
        }
      ],
      "source": [
        "logdir = \"logs/scalars/\" + datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
        "tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)\n",
        "\n",
        "model = keras.models.Sequential([\n",
        "    keras.layers.Dense(16, input_dim=1),\n",
        "    keras.layers.Dense(1),\n",
        "])\n",
        "\n",
        "model.compile(\n",
        "    loss='mse', # keras.losses.mean_squared_error\n",
        "    optimizer=keras.optimizers.SGD(lr=0.2),\n",
        ")\n",
        "\n",
        "print(\"Training ... With default parameters, this takes less than 10 seconds.\")\n",
        "training_history = model.fit(\n",
        "    x_train, # input\n",
        "    y_train, # output\n",
        "    batch_size=train_size,\n",
        "    verbose=0, # Suppress chatty output; use Tensorboard instead\n",
        "    epochs=100,\n",
        "    validation_data=(x_test, y_test),\n",
        "    callbacks=[tensorboard_callback],\n",
        ")\n",
        "\n",
        "print(\"Average test loss: \", np.average(training_history.history['loss']))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "042k7GMERVkx"
      },
      "source": [
        "## TensorBoard を使って損失を調べる\n",
        "\n",
        "では、上記で使用したルートログディレクトリを指定して、TensorBoard を起動しましょう。\n",
        "\n",
        "TensorBoard の UI が読み込まれるまで数秒ほどかかります。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6pck56gKReON"
      },
      "outputs": [],
      "source": [
        "%tensorboard --logdir logs/scalars"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QmQHlG10Kpu2"
      },
      "source": [
        " <img src=\"https://github.com/tensorflow/tensorboard/blob/master/docs/images/scalars_loss.png?raw=1\" class=\"\"> "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ciSIRibhRi6N"
      },
      "source": [
        "TensorBoard に「No dashboards are active for the current data set（現在のデータセットにはアクティブなダッシュボードはありません）」というメッセージが表示されることがあります。これは、最初のログデータがまだ保存されていないためです。トレーニングが進むにつれ、Keras モデルはデータをログし始めるため、TensorBoard の再読み込みが定期的に行われ、スカラーメトリックが表示されるようになります。それまで待てない方は、右上にある再読み込み矢印をタップしてください。\n",
        "\n",
        "トレーニングが進むにつれ、トレーニングと検証の損失が急速に下降し、安定する様子が見られます。実際、トレーニングが 25 エポックを終了したところであまり改善が見られなくなるため、その時点でトレーニングを停止することができます。\n",
        "\n",
        "グラフをホバーし、特定のデータポイントを確認します。マウスを使用して拡大したり、部分的に選択して詳細を確認することも可能です。\n",
        "\n",
        "左側に「Runs（実行）」セレクタがあります。1 つの「実行」は、1 ラウンドのトレーニングから得たログ一式を指します。このケースでは、これは Model.fit() の結果です。通常、長期間にわたってモデルの実験と開発が行われるため、実行の数は非常に多くなります。\n",
        "\n",
        "Runs セレクタを使用して特定の実行を選択するか、トレーニングまたは検証のみから選択します。実行を比較すると、どのバージョンのコードで問題の解決が最適に行われているかを評価しやすくなります。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "finK0GfYyefe"
      },
      "source": [
        "TensorBoard の損失グラフには、トレーニングと検証の両方で損失が一定して下降し、安定したことが示されています。つまり、モデルのメトリックは非常に良質である可能性が高いということです。では、このモデルが実際のデータでどのように動作するかを見てみましょう。\n",
        "\n",
        "入力データ (60, 25, 2) がある場合、線 *y = 0.5x + 2* は (32, 14.5, 3) となるはずです。モデルはこれと合致するでしょうか。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "id": "EuiLgxQstt32"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[32.234306 ]\n",
            " [14.5974245]\n",
            " [ 3.0074697]]\n"
          ]
        }
      ],
      "source": [
        "print(model.predict([60, 25, 2]))\n",
        "# True values to compare predictions against: \n",
        "# [[32.0]\n",
        "#  [14.5]\n",
        "#  [ 3.0]]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bom4MdeewRKS"
      },
      "source": [
        "よくできました！"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vvwGmJK9XWmh"
      },
      "source": [
        "## カスタムスカラーをログする\n",
        "\n",
        "[動的学習率](https://www.jeremyjordan.me/nn-learning-rate/)などのカスタムの値をログする場合はどうでしょうか。この場合は、TensorFlow Summary API を使用する必要があります。\n",
        "\n",
        "回帰モデルを維持したまま、カスタム学習率をログします。次のように行ってください。\n",
        "\n",
        "1. `tf.summary.create_file_writer()` を使ってファイルライターを作成します。\n",
        "2. カスタム学習率の関数を定義します。この関数は、Keras の [LearningRateScheduler](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/LearningRateScheduler) コールバックに渡されます。\n",
        "3. 学習率関数内に、カスタム学習率をログするための `tf.summary.scalar()` を使用します。\n",
        "4. LearningRateScheduler コールバックを Model.fit() に渡します。\n",
        "\n",
        "一般的に、カスタムスカラーをログするには、`tf.summary.scalar()` をファイルライターとともに使用する必要があります。この実行のデータを特定のディレクトリに書き込むのはファイルライターであり、`tf.summary.scalar()` を使用する際にファイルライターが暗黙的に使用されるためです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XB95ltRiXVXk"
      },
      "outputs": [],
      "source": [
        "logdir = \"logs/scalars/\" + datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
        "file_writer = tf.summary.create_file_writer(logdir + \"/metrics\")\n",
        "file_writer.set_as_default()\n",
        "\n",
        "def lr_schedule(epoch):\n",
        "  \"\"\"\n",
        "  Returns a custom learning rate that decreases as epochs progress.\n",
        "  \"\"\"\n",
        "  learning_rate = 0.2\n",
        "  if epoch > 10:\n",
        "    learning_rate = 0.02\n",
        "  if epoch > 20:\n",
        "    learning_rate = 0.01\n",
        "  if epoch > 50:\n",
        "    learning_rate = 0.005\n",
        "\n",
        "  tf.summary.scalar('learning rate', data=learning_rate, step=epoch)\n",
        "  return learning_rate\n",
        "\n",
        "lr_callback = keras.callbacks.LearningRateScheduler(lr_schedule)\n",
        "tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)\n",
        "\n",
        "model = keras.models.Sequential([\n",
        "    keras.layers.Dense(16, input_dim=1),\n",
        "    keras.layers.Dense(1),\n",
        "])\n",
        "\n",
        "model.compile(\n",
        "    loss='mse', # keras.losses.mean_squared_error\n",
        "    optimizer=keras.optimizers.SGD(),\n",
        ")\n",
        "\n",
        "training_history = model.fit(\n",
        "    x_train, # input\n",
        "    y_train, # output\n",
        "    batch_size=train_size,\n",
        "    verbose=0, # Suppress chatty output; use Tensorboard instead\n",
        "    epochs=100,\n",
        "    validation_data=(x_test, y_test),\n",
        "    callbacks=[tensorboard_callback, lr_callback],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pck8OQEjayDM"
      },
      "source": [
        "もう一度 TensorBoard を確認しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0sjM2wXGa0mF"
      },
      "outputs": [],
      "source": [
        "%tensorboard --logdir logs/scalars"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GkIahGZKK9I7"
      },
      "source": [
        " <img src=\"https://github.com/tensorflow/tensorboard/blob/master/docs/images/scalars_custom_lr.png?raw=1\" class=\"\"> "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RRlUDnhlkN_q"
      },
      "source": [
        "左側の「Runs」セレクタを使用すると、`<timestamp>/metrics` 実行が 1 つあります。この実行を選択すると「learning rate（学習率）」グラフが表示され、この実行における学習率の進行を確認することができます。\n",
        "\n",
        "また、この実行のトレーニングと検証の損失曲線を、以前の実行のものと比較することも可能です。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l0TTI16Nl0nk"
      },
      "source": [
        "このモデルの出来栄えはどうでしょうか。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "id": "97T4vT3QkQJH"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[32.234013 ]\n",
            " [14.5973015]\n",
            " [ 3.0074618]]\n"
          ]
        }
      ],
      "source": [
        "print(model.predict([60, 25, 2]))\n",
        "# True values to compare predictions against: \n",
        "# [[32.0]\n",
        "#  [14.5]\n",
        "#  [ 3.0]]"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "scalars_and_keras.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
